package system

import (
	"context"
	"slices"
	"strconv"

	"portal/internal/errorx"
	"portal/internal/svc"
	"portal/internal/tool/auth"
	"portal/internal/types"
	"portal/model"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/mr"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

type ListDeptLogic struct {
	logx.Logger
	ctx       context.Context
	svcCtx    *svc.ServiceContext
	deptModel model.SysDeptModel
	menuModel model.SysMenuModel
}

func NewListDeptLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListDeptLogic {
	return &ListDeptLogic{
		Logger:    logx.WithContext(ctx),
		ctx:       ctx,
		svcCtx:    svcCtx,
		deptModel: model.NewSysDeptModel(svcCtx.Orm),
		menuModel: model.NewSysMenuModel(svcCtx.Orm),
	}
}

func (l *ListDeptLogic) ListDept(params *types.ListDeptRequest) (resp *types.CommonResponse, err error) {
	// step2 校验用户权限
	err = auth.HasPermission(l.menuModel, l.ctx, "system:dept:query")
	if err != nil {
		return nil, errorx.Unauthorized
	}
	// 即便拥有权限也需要判断哪些部门信息可以查看
	// 此为获取可管理的部门信息列表
	var deptvos []*types.Dept
	isAdmin, err := auth.IsAdmin(l.ctx)
	if err != nil {
		return nil, err
	}
	if !isAdmin {
		roles, err := auth.GetRoles(l.ctx)
		if err != nil {
			return nil, err
		}
		strDeptId, err := auth.GetDeptId(l.ctx)
		if err != nil {
			return nil, err
		}
		deptId, _ := strconv.Atoi(strDeptId)
		deptvos,err = getDepts(l.ctx, int64(deptId), roles, l.deptModel, params)
		if err != nil {
			return nil, errorx.NewBizError(500, err.Error())
		}
	} else {
		deptdtos, err := l.deptModel.GetList(l.ctx, nil, params)
		if err != nil && err != sqlx.ErrNotFound {
			return nil, errorx.NewBizError(500, err.Error())
		}
		deptlist := make([]*types.Dept, 0)
		for _, deptdto := range deptdtos {
			deptlist = append(deptlist, &types.Dept{
				DeptId:     deptdto.DeptId,
				ParentId:   strconv.Itoa(int(deptdto.ParentId)),
				DeptName:   deptdto.DeptName,
				OrderNum:   deptdto.OrderNum,
				Leader:     deptdto.Leader.String,
				Email:      deptdto.Email.String,
				Phone:      deptdto.Phone.String,
				Status:     deptdto.Status,
				DelFlag:    deptdto.DelFlag,
				CreateBy:   deptdto.CreateBy,
				CreateTime: deptdto.CreateTime,
				UpdateTime: deptdto.UpdateTime,
			})
		}
		deptvos = deptlist
	}
	depts := deptSort(deptvos)
	return &types.CommonResponse{
		Status: 200,
		Msg:    "查询成功",
		Data:   depts,
	}, nil
}

func getDepts(ctx context.Context, deptId int64, roles []*types.Role, deptModel model.SysDeptModel, params *types.ListDeptRequest) ([]*types.Dept,error) {
	ma := func(source chan<- interface{}) {
		for _, v := range roles {
			source <- v
		}
	}
	mb := func(item interface{}, writer mr.Writer[[]*model.SysDept], cancel func(err error)) {
		tmp := item.(*types.Role)
		var err error
		var depts []*model.SysDept
		switch tmp.DataScope {
		// 自定义权限
		case "2":
			depts, err = deptModel.GetListAuto(ctx, nil, tmp.RoleId, params)
		case "3":
			depts, err = deptModel.GetListBase(ctx, nil, int64(deptId), true)
		case "4":
			depts, err = deptModel.GetListBase(ctx, nil, int64(deptId), false)
		}
		if err != nil && err != sqlx.ErrNotFound {
			return
		}
		writer.Write(depts)
	}
	mc := func(pipe <-chan []*model.SysDept, writer mr.Writer[[]*types.Dept], cancel func(err error)) {
		depts := make(map[int64]*types.Dept)
		for v := range pipe {
			deptdtos := v

			for _, deptdto := range deptdtos {
				depts[deptdto.DeptId] = &types.Dept{
					DeptId:   deptdto.DeptId,
					DeptName: deptdto.DeptName,
					OrderNum: deptdto.OrderNum,
					Leader:   deptdto.Leader.String,
					Email:    deptdto.Email.String,
					Phone:    deptdto.Phone.String,
					Status:   deptdto.Status,
					DelFlag:  deptdto.DelFlag,
					ParentId: strconv.Itoa(int(deptdto.ParentId)),
				}
			}

		}
		deptlist := make([]*types.Dept, 0)
		for _, value := range depts {
			deptlist = append(deptlist, value)
		}
		writer.Write(deptlist)
	}
	deptvos, err := mr.MapReduce(ma, mb, mc)
	return deptvos,err
}

func deptSort(depts []*types.Dept) *types.Dept {
	// 三级目录
	slices.SortFunc(depts, func(a, b *types.Dept) int {
		return int(a.OrderNum - b.OrderNum)
	})
	m := make(map[int64]*types.Dept)
	for _, val := range depts {
		m[val.DeptId] = val
	}
	rootDept := &types.Dept{
		DeptId: 0,
		Depts:  make([]*types.Dept, 0),
	}
	m[rootDept.DeptId] = rootDept

	for _, val := range depts {
		val.Depts = make([]*types.Dept, 0)
	}

	for _, val := range depts {
		parentId, _ := strconv.Atoi(val.ParentId)
		if m[int64(parentId)] == nil {
			m[int64(0)].Depts = append(m[int64(0)].Depts, val)
			continue
		}
		m[int64(parentId)].Depts = append(m[int64(parentId)].Depts, val)
	}
	return rootDept
}
